/*
 * Copyright (c) 2009 Nathaniel Houghton <nathan@brainwerk.org>
 *                    Robert Kirchgessner <rkirchge@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for
 * any purpose with or without fee is hereby granted, provided that
 * the above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>

#include "usart.h"
#include "util.h"
#include "lcd.h"

/* devices */
#define XBEE 0
#define ELM 1

/* PB0 is cts, PB1 is rts (both low true) */
#define usart0_cts_set(void) (PORTB &= 0xFE)
#define usart0_cts_clr(void) (PORTB |= 0x01)
#define usart0_rts(void) ((PINB & 0x02) ? 0 : 1)

static volatile struct usart_rxbuf rx_buf[2];
static volatile struct usart_txbuf tx_buf[2];

void
usart_init(void)
{
	memset(&rx_buf, 0, sizeof(rx_buf));
	memset(&tx_buf, 0, sizeof(tx_buf));

	rx_buf[XBEE].echo = 1;

	/* USART0 (XBEE) */
	/* 57.6 kbps */
	UBRR0H = 0x00;
	UBRR0L = 0x10;
	UCSR0A = 0x02;
	/* rx and tx enable, rx interrupt */
	UCSR0B = 0x98;
	/* asynchronous, 8 bit, no parity */
	UCSR0C = 0x06;

	/* USART1 (ELM) */
	/* 38.4 kbps */
	UBRR1H = 0x00;
	UBRR1L = 0x0C;
	UCSR1A = 0x00;
	/* rx and tx enable, rx interrupt */
	UCSR1B = 0x98;
	/* asynchronous, 8 bit, no parity */
	UCSR1C = 0x06;

	/* set pin PB0 (cts) to an output */
	DDRB |= 0x01;

	/* enable PCINT 9 (PB1) for rts */
	PCMSK1 = 0x02;
	PCICR = 0x02;
}

#define rx_int_disable(dev) if (dev == 0) UCSR0B &= 0x7F; else UCSR1B &= 0x7F;
#define rx_int_enable(dev) if (dev == 0) UCSR0B |= 0x80; else UCSR1B |= 0x80;

uint16_t
usart_rx(uint8_t dev, uint8_t *dst, uint16_t size)
{
	uint16_t r;

	r = buf_rem(dev, dst, size);

	return r;
}

#define udre_int_disable(dev) if (dev == 0) UCSR0B &= 0xDF; else UCSR1B &= 0xDF;
#define udre_int_enable(dev) if (dev == 0) UCSR0B |= 0x20; else UCSR1B |= 0x20;

uint16_t
usart_tx(uint8_t dev, uint8_t *src, uint16_t len)
{
	uint16_t appended;

	appended = buf_cat(dev, src, len);

	return appended;
}

uint16_t
buf_cat(uint8_t dev, uint8_t *src, uint16_t len)
{
	uint16_t appended;
	uint16_t chunk_size;
	volatile struct usart_txbuf *txb = &tx_buf[dev];
	
	if (userland_wait())
		return 0;
	
	appended = 0;

	txb->inuse = 1;

	/*
	 * Pause TX and RX while modifying buffer.
	 * RX int disabled because RX int can modify the TX buffer
	 * (echo) and also restart udre
	 */
	rx_int_disable(dev);
	udre_int_disable(dev);

	/* adjust len to what we can actually append */
	if (len > USART_TXBUFSIZE - txb->used)
		len = USART_TXBUFSIZE - txb->used;

	while (len != 0) {
		if (txb->tail >= txb->head)
			chunk_size = USART_TXBUFSIZE - txb->tail;
		else
			chunk_size = txb->head - txb->tail + 1;

		if (chunk_size > len)
			chunk_size = len;

		memcpy((struct usart_txbuf *) &txb->buf[txb->tail],
		    src, chunk_size);

		src += chunk_size;
		len -= chunk_size;
		txb->tail += chunk_size;
		appended += chunk_size;
		txb->used += chunk_size;

		if (txb->tail >= USART_TXBUFSIZE)
			txb->tail = 0;
	}

	txb->inuse = 0;
	
	udre_int_enable(dev);
	rx_int_enable(dev);

	return appended;
}

int
usart_getc(uint8_t dev)
{
	uint8_t c;
	uint16_t r;

	do {
		r = usart_rx(dev, &c, sizeof(c));
	} while (r != sizeof(c));

	return c;
}

void
usart_putc(uint8_t dev, int c)
{
	uint16_t r;
	uint8_t d;

	d = c;
	do {
		r = usart_tx(dev, &d, sizeof(d));
	} while (r != sizeof(d));
}

void
usart_put(uint8_t dev, const void *vbuf, uint16_t len)
{
	uint16_t sent;
	uint16_t r;
	uint8_t *buf;

	buf = (uint8_t *) vbuf;

	sent = 0;
	while (sent < len) {
		r = usart_tx(dev, (uint8_t *) buf, len - sent);
		buf += r;
		sent += r;
	}
}

void
usart_puts(uint8_t dev, const char *str)
{
	usart_put(dev, (uint8_t *) str, strlen(str));
}

uint16_t
usart_rxused(uint8_t dev)
{
	volatile struct usart_rxbuf *rxb = &rx_buf[dev];
	uint16_t used;

	cli();
	used = rxb->used;
	sei();

	return used;
}

int8_t
usart_peek(uint8_t dev, char c)
{
	volatile struct usart_rxbuf *rxb = &rx_buf[dev];
	int i, j;
	int found = 0;

	cli();

	i = rxb->head;
	for (j = rxb->used; j > 0; --j) {
		if (rxb->buf[i] == c) {
			found = 1;
			break;
		}

		++i;
		if (i >= USART_RXBUFSIZE)
			i = 0;
	}

	if (!found && rxb->used >= USART_CTS_MAX)
		found = -1;

	sei();

	return found;
}

void
usart_flush(uint8_t dev)
{
	volatile struct usart_rxbuf *rxb = &rx_buf[dev];

	cli();

	rxb->used = rxb->head = rxb->tail = 0;
	if (dev == 0)
		usart0_cts_set();

	sei();
}

uint16_t
buf_rem(uint8_t dev, uint8_t *dst, uint16_t len)
{
	uint16_t removed;
	volatile struct usart_rxbuf *rxb = &rx_buf[dev];
	
	if (userland_wait())
		return 0;

	removed = 0;

	rx_int_disable(dev);

	if (rxb->used < len)
		len = rxb->used;
	
	rx_int_enable(dev);

	removed = len;

	while (len > 0) {
		*dst = rxb->buf[rxb->head];

		++rxb->head;
		--len;
		++dst;

		if (rxb->head >= USART_RXBUFSIZE)
			rxb->head = 0;
	}

	rx_int_disable(dev);

	rxb->used -= removed;

	rx_int_enable(dev);
	
	if (dev == 0) {
		if (rx_buf[XBEE].used <= USART_CTS_MIN)
			usart0_cts_set();
	}

	return removed;
}

ISR(USART0_RX_vect)
{
	volatile struct usart_rxbuf *rxb = &rx_buf[XBEE];
	volatile struct usart_txbuf *txb = &tx_buf[XBEE];

	if (UCSR0A & 0x08)
		fatal("rx0 overflow");

	if (rxb->used == USART_RXBUFSIZE)
		fatal("RX0 buffer full!");

#if 1
	if (rxb->used >= USART_CTS_MAX)
		usart0_cts_clr();
#endif

	rxb->buf[rxb->tail] = UDR0;

#if 1
	if (rxb->echo) {
		if (txb->used + 1 < USART_TXBUFSIZE) {
			uint8_t c;

			c = txb->buf[txb->tail] = rxb->buf[rxb->tail];
			++txb->tail;
			++txb->used;

			if (txb->tail >= USART_TXBUFSIZE)
				txb->tail = 0;

			if (c == '\r') {
				txb->buf[txb->tail] = '\n';
				++txb->tail;
				++txb->used;

				if (txb->tail >= USART_TXBUFSIZE)
					txb->tail = 0;
			}
		}
		/* this interrupt cannot run if we are in userland tx routines */
		udre_int_enable(XBEE);
	}
#endif

	++rxb->used;
	++rxb->tail;

	if (rxb->tail >= USART_RXBUFSIZE)
		rxb->tail = 0;
	
	return;
}

/* PB1 / PCINT9 pin change interrupt (for rts) */
ISR(PCINT1_vect)
{
	/* remote buffer ready, and we have something to send */
	if (usart0_rts() && tx_buf[XBEE].used > 0) {
		if (tx_buf[XBEE].inuse == 0) {
			udre_int_enable(XBEE);
		}
	}
}

ISR(USART0_UDRE_vect)
{
	volatile struct usart_txbuf *txb = &tx_buf[XBEE];

	/*
	 * Remote buffer full or nothing more to send, pause
	 * transmission.
	 */
	if (!usart0_rts() || txb->used == 0) {
		udre_int_disable(XBEE);
		return;
	}

	UDR0 = txb->buf[txb->head];

	--txb->used;
	++txb->head;

	if (txb->head >= USART_TXBUFSIZE)
		txb->head = 0;

	return;
}

ISR(USART1_RX_vect)
{
	volatile struct usart_rxbuf *rxb = &rx_buf[ELM];

	if (UCSR1A & 0x08)
		fatal("rx1 overflow");

	if (rxb->used == USART_RXBUFSIZE)
		fatal("RX1 buffer full!");

	rxb->buf[rxb->tail] = UDR1;

	++rxb->used;
	++rxb->tail;

	if (rxb->tail >= USART_RXBUFSIZE)
		rxb->tail = 0;

	return;
}

ISR(USART1_UDRE_vect)
{
	volatile struct usart_txbuf *txb = &tx_buf[ELM];

	/* nothing more to send, disable this interrupt */
	if (txb->used == 0) {
		udre_int_disable(ELM);
		return;
	}

	UDR1 = txb->buf[txb->head];

	--txb->used;
	++txb->head;

	if (txb->head >= USART_TXBUFSIZE)
		txb->head = 0;

	return;
}

void
usart_echo(uint8_t dev, uint8_t setting)
{
	volatile struct usart_rxbuf *rxb = &rx_buf[dev];

	rxb->echo = setting;
}

uint8_t
userland_wait(void)
{
	return 0;
}
